/*
    生产者消费者模型
*/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>

//创建一个互斥量,解决数据同步问题
pthread_mutex_t mutex;
//创建一个条件变量用于通知操作
pthread_cond_t cond;

//生产者——消费者队列，里面存放节点表示任务
struct Node
{
    int num;
    struct Node* next;
};

struct Node* head = NULL;

void* producer(void* arg){
    //不断地创建新节点，添加到链表中
    while(1){
        //加锁
        pthread_mutex_lock(&mutex);
        struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
        //采用头插法
        newNode->next = head;
        head = newNode;
        newNode->num = rand()%100;
        printf("add node, num : %d, tid: %ld\n", newNode->num,pthread_self());

        //只要生产了一个，就通知消费者消费
        pthread_cond_signal(&cond);

        //解锁
        pthread_mutex_unlock(&mutex);
        usleep(100);
    }
    return NULL;
}

void* customer(void* arg){
    while(1){
        //加锁
        pthread_mutex_lock(&mutex);
        //保存头节点的指针
        struct Node* temp = head;
        //判断是否有数据
        if(head != NULL){
            head = head->next;
            printf("del node, num : %d, tid : %ld\n", temp->num, pthread_self());
            free(temp);
            //解锁
            pthread_mutex_unlock(&mutex);
            usleep(100);
        }else{
            //没有数据，原地等待
            //pthread_cond_wait()函数调用阻塞地时候，会对互斥锁进行解锁，当不阻塞地时候，继续向下执行，进行加锁
            pthread_cond_wait(&cond, &mutex);
            pthread_mutex_unlock(&mutex);
        }
    }
    return NULL;
}

int main(){

    //初始化互斥量
    pthread_mutex_init(&mutex,NULL);
    //初始化条件变量
    pthread_cond_init(&cond,NULL);

    //创建5个生产者线程，5个消费者线程
    pthread_t ptid[5], ctid[5];
    for(int i = 0; i < 5; ++i){
        pthread_create(&ptid[i],NULL,producer,NULL);
        pthread_create(&ctid[i],NULL,customer,NULL);
    } 

    //设置线程分离，主线程退出后，子线程仍可以运行
    for(int i = 0; i < 5; ++i){
        pthread_detach(ptid[i]);
        pthread_detach(ctid[i]);
    }

    while(1){
        sleep(10);         //为了确保能执行到销毁互斥量
    }

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    //主线程退出
    pthread_exit(NULL);

    return 0;
}

